# -*- coding: utf-8 -*-
import numpy as np
from abc import ABCMeta, abstractmethod
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.utils import check_random_state


class SemiSupervisedClassifier(BaseEstimator, ClassifierMixin):
    __metaclass__ = ABCMeta

    def __init__(self, base_estimator, unlabeled_data, unlabeled_label, random_state=None):
        super().__init__()
        self.base_estimator = base_estimator
        self.unlabeled_data = np.array(unlabeled_data)
        self.unlabeled_label = np.array(unlabeled_label).flatten()
        self.random_state = random_state
        # 默认基学习器数量为3
        self.n_estimator = 3
        self.estimators_ = []
        # 记录训练中每次循环在训练集上的准确率
        self.train_scores = []

    def fit(self, X, y):
        X = np.array(X)
        y = np.array(y).flatten()
        if len(self.estimators_) <= 0:
            self.init_estimators(X, y)
        self._update_estimator(X, y)
        return self

    def predict(self, X):
        predictions = [estimator.predict(X) for estimator in self.estimators_]
        predictions = np.array(predictions)
        vote_predictions = np.sum(predictions, axis=0)
        return np.where(vote_predictions > 0, 1, -1)

    def init_estimators(self, X, y):
        self.estimators_ = []
        clazz = getattr(self.base_estimator, '__class__')
        params = self.base_estimator.get_params()
        for i in range(self.n_estimator):
            estimator = clazz(**params)
            # samples, labels = X, y
            samples, labels = self._bootstrap_sampling(X, y)
            estimator.fit(samples, labels)
            self.estimators_.append(estimator)

    @abstractmethod
    def _update_estimator(self, X, y):
        pass

    @staticmethod
    def _bootstrap_sampling(X, y=None, size=None, random_state=None):
        """
        Params
        -------
            X: 不含标签的数据，形状为2-D
            y: 数据标签，默认值为None时，不返回y的采样。形状为1-D
            size: 取样大小，默认为与X同样大小
            random_state: 随机的seed
        Return
        -------
            数据的采样
        """
        random_state = check_random_state(random_state)
        if size is None:
            size = len(X)
        samples = []
        labels = []
        for i in range(size):
            inx = int(random_state.rand() * len(X))
            samples.append(X[inx])
            if y is not None:
                labels.append(y[inx])
        if y is not None:
            return np.array(samples), np.array(labels)
        else:
            return np.array(samples)


if __name__ == '__main__':
    pass
